# !/usr/bin/env python
# encoding: utf-8
"""
@author:zwb
@file: middleware.py
@contact: zwb15083976291@163.com
@time: 2021/10/09
"""
import os
import time
import re

from django.conf import settings
from django.http import JsonResponse
from django.utils.deprecation import MiddlewareMixin
from rest_framework import status

from common.models import BlogUser
from utils.redis_clients import RedisClient
from utils.responses import CommonResponse, CommonAPIResponseStatus
from utils.responses.response import response_data
from utils.token import TokenGenerator


class SpendTimeMiddleware(MiddlewareMixin):

    def __call__(self, request):
        response = None
        start_time = time.time()

        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        response = response or self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        usage_time = time.time() - start_time
        if os.environ.get("BLOG_PROJECT_ENV", "") == "dev":
            print(f"本次请求花费时间:{round(usage_time * 100, 3)} ms")

        return response


class AuthenticateMiddleWare(object):
    """验证登录中间件"""

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.process_request(request)
        response = response or self.get_response(request)
        response = self.process_response(request, response)
        return response

    def process_request(self, request):
        authorization = request.headers.get('authorization')
        if not authorization:
            return None

        ret, data = TokenGenerator(secret_key=getattr(settings, "SECRET_KEY"),
                                   salt=getattr(settings, "TOKEN_SALT")).decrypt_token(authorization)
        if not ret:
            return JsonResponse(data=response_data(
                CommonAPIResponseStatus.TOKEN_EXPIRES if data[-1] else CommonAPIResponseStatus.TOKEN_ERROR))
        token = RedisClient().get_decode_response_or_not(key=getattr(settings, "BLOG_SALT_KEY"))
        if token != authorization:
            return JsonResponse(response_data(CommonAPIResponseStatus.OTHER_CLIENTS_LOGGED))
        admin = BlogUser.objects.filter(id=data.get('id'), username=data.get("username")).first()
        if admin:
            request.auth_user = admin
        else:
            return JsonResponse(response_data(CommonAPIResponseStatus.SERVICE_UNAVAILABLE))

    def process_view(self, request, view_func, view_args, view_kwargs):
        # print("Md1在执行%s视图前" % view_func.__name__)
        pass

    def process_exception(self, request, exception):
        # print("Md1处理视图异常...")
        pass

    def process_response(self, request, response):
        return response


class ThrottleMiddleWare(object):

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.process_request(request)
        response = response or self.get_response(request)
        response = self.process_response(request, response)
        return response

    def process_request(self, request):
        # 使用令牌限流桶
        redis_key = getattr(settings, 'TOKEN_CURRENT_LIMITING_KEY')  # 限流的redis key
        time_zone = getattr(settings, 'TOKEN_CURRENT_LIMITING_TIME_ZONE')  # 限流的限制时间
        capacity = getattr(settings, 'TOKEN_CURRENT_LIMITING_CAPACITY')  # 限制时间的允许访问次数

        if not token_current_limiting_bucket(redis_key, time_zone, capacity):
            return JsonResponse(response_data(CommonAPIResponseStatus.TOO_MANY_REQUESTS),
                                status=status.HTTP_429_TOO_MANY_REQUESTS)

    def process_response(self, request, response):
        return response


def token_current_limiting_bucket(redis_key, time_zone=60, capacity=60) -> bool:
    """
    令牌限流桶函数
    :param redis_key: redis_key
    :param item_zone: 限制时间段，默认60秒
    :param capacity:  限制时间内允许访问的次数，默认60
    :return:
    """
    # 每秒的生成速度
    rate = capacity / time_zone
    redis_client = RedisClient()
    # 获取到上次剩下的访问数量
    remain_number = redis_client.hget__decode_response_or_not(name=redis_key, key="remain_number")
    last_time = redis_client.hget__decode_response_or_not(name=redis_key, key='last_time')
    if not remain_number and not last_time:
        with RedisClient().pipeline() as pipeline:
            pipeline.hset(name=redis_key, key='remain_number', value=capacity)  # 剩余访问量
            pipeline.hset(name=redis_key, key='last_time', value=time.time())  # 添加存储的时间
            pipeline.expire(name=redis_key, time=time_zone)  # 设置过期时间
            pipeline.execute()

    remain_number = int(remain_number) if remain_number else capacity
    last_time = float(last_time) if last_time else time.time()

    # 令牌的增量
    token_increment = int(rate * (time.time() - last_time))
    if token_increment >= 1:
        remain_number += token_increment
        if remain_number > capacity:
            remain_number = capacity
        with RedisClient().pipeline() as pipeline:
            pipeline.hset(name=redis_key, key='last_time', value=time.time())
            pipeline.expire(name=redis_key, time=time_zone)  # 设置过期时间
            pipeline.execute()

    if remain_number >= 1:
        remain_number -= 1
        with RedisClient().pipeline() as pipeline:
            pipeline.hset(name=redis_key, key='remain_number', value=remain_number)
            pipeline.expire(name=redis_key, time=time_zone)  # 设置过期时间
            pipeline.execute()
        return True
    return False
